﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Diagnostics;
using Lilium.Web.Mvc.MicrosoftAjax;
using System.Web.Script.Serialization;

namespace Lilium.Web.Mvc
{
	internal sealed class ScriptResourceData
	{
		public static ScriptResourceData EnsureResourceData(AjaxHelper ajaxHelper)
		{
			if (ajaxHelper == null)
				throw new ArgumentNullException("ajaxHelper");

			var items = ajaxHelper.ViewContext.HttpContext.Items;
			var scriptResourceData = (ScriptResourceData)items[typeof(ScriptResourceData)];
			if (scriptResourceData == null)
			{
				scriptResourceData = new ScriptResourceData();
				items[typeof(ScriptResourceData)] = scriptResourceData;
			}
			return scriptResourceData;
		}

		/// <summary>
		/// Returns script resource data or null if no resource data associated with 
		/// current request.
		/// </summary>
		/// <param name="ajaxHelper"></param>
		/// <returns></returns>
		public static ScriptResourceData GetResourceData(AjaxHelper ajaxHelper)
		{
			if (ajaxHelper == null)
				throw new ArgumentNullException("ajaxHelper");

			return (ScriptResourceData)ajaxHelper.ViewContext.HttpContext.Items[typeof(ScriptResourceData)];
		}

		public void RegisterDataItem(string id, object data)
		{
			DataItems[id] = data;
		}

		public void RegisterClientScript(AjaxHelper ajaxHelper, IScriptDescriptor scriptDescriptor)
		{
			ClientScripts.Add(scriptDescriptor);
		}

		public void RenderClientScripts(AjaxHelper ajaxHelper)
		{
			if (ajaxHelper == null)
				throw new ArgumentNullException("ajaxHelper");

			var output = ajaxHelper.ViewContext.HttpContext.Response.Output;
			var scriptTag = new TagBuilder("script");
			scriptTag.MergeAttribute("type", "text/javascript");
			output.WriteLine(scriptTag.ToString(TagRenderMode.StartTag));
			output.WriteLine("//<![CDATA[");
			foreach (var clientScript in ClientScripts)
			{
				foreach (var resourceReference in clientScript.GetResourceReferences())
					if (!RenderedResources.Contains(resourceReference))
						throw new InvalidOperationException(string.Format(
							Resources.Culture,
							Resources.ScriptResourceData_ScriptResourceReferenceNotRendered_0reference_1clientScript,
							resourceReference, clientScript));
				clientScript.RenderScript(ajaxHelper);
			}
			output.WriteLine("//]]>");
			output.WriteLine(scriptTag.ToString(TagRenderMode.EndTag));
		}

		public void RenderDataItems(AjaxHelper ajaxHelper)
		{
			if (ajaxHelper == null)
				throw new ArgumentNullException("ajaxHelper");

			if (DataItems.Count != 0)
			{
				var tagBuilder = new TagBuilder("input");
				tagBuilder.MergeAttribute("type", "hidden");
				tagBuilder.MergeAttribute("id", "__Lilium_SDI");
				tagBuilder.MergeAttribute("value", (new JavaScriptSerializer()).Serialize(DataItems));
				ajaxHelper.ViewContext.HttpContext.Response.Output.WriteLine(tagBuilder.ToString(TagRenderMode.SelfClosing));
			}
		}

		public void RenderScriptResourceReference(AjaxHelper ajaxHelper, IScriptResourceReference scriptResourceReference)
		{
			if (scriptResourceReference == null)
				throw new ArgumentNullException("scriptResourceReference");
			if (RenderedResources.Add(scriptResourceReference))
				scriptResourceReference.Render(ajaxHelper);
		}

		#region private Dictionary<string, object> DataItems

		private Dictionary<string, object> m_DataItems = new Dictionary<string,object>();

		private Dictionary<string, object> DataItems { [DebuggerNonUserCodeAttribute] get { return m_DataItems; } [DebuggerNonUserCodeAttribute] set { m_DataItems = value; } }

		#endregion

		#region private HashSet<ScriptResourceReferenceBase> RenderedResources

		private readonly HashSet<IScriptResourceReference> m_RenderedResources = new HashSet<IScriptResourceReference>();

		private HashSet<IScriptResourceReference> RenderedResources { [DebuggerNonUserCodeAttribute] get { return m_RenderedResources; } }

		#endregion

		#region private List<IScriptDescriptor> ClientScripts

		private readonly List<IScriptDescriptor> m_ClientScripts = new List<IScriptDescriptor>();

		private List<IScriptDescriptor> ClientScripts { [DebuggerNonUserCodeAttribute] get { return m_ClientScripts; } }

		#endregion
	}
}
